<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<!--[if IE]><meta http-equiv="X-UA-Compatible" content="IE=edge"><![endif]-->
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="generator" content="Asciidoctor 1.5.5.dev">
<title>http router</title>
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Open+Sans:300,300italic,400,400italic,600,600italic%7CNoto+Serif:400,400italic,700,700italic%7CDroid+Sans+Mono:400,700">
<style>
/* Asciidoctor default stylesheet | MIT License | http://asciidoctor.org */
/* Remove comment around @import statement below when using as a custom stylesheet */
/*@import "https://fonts.googleapis.com/css?family=Open+Sans:300,300italic,400,400italic,600,600italic%7CNoto+Serif:400,400italic,700,700italic%7CDroid+Sans+Mono:400,700";*/
article,aside,details,figcaption,figure,footer,header,hgroup,main,nav,section,summary{display:block}
audio,canvas,video{display:inline-block}
audio:not([controls]){display:none;height:0}
[hidden],template{display:none}
script{display:none!important}
html{font-family:sans-serif;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}
body{margin:0}
a{background:transparent}
a:focus{outline:thin dotted}
a:active,a:hover{outline:0}
h1{font-size:2em;margin:.67em 0}
abbr[title]{border-bottom:1px dotted}
b,strong{font-weight:bold}
dfn{font-style:italic}
hr{-moz-box-sizing:content-box;box-sizing:content-box;height:0}
mark{background:#ff0;color:#000}
code,kbd,pre,samp{font-family:monospace;font-size:1em}
pre{white-space:pre-wrap}
q{quotes:"\201C" "\201D" "\2018" "\2019"}
small{font-size:80%}
sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}
sup{top:-.5em}
sub{bottom:-.25em}
img{border:0}
svg:not(:root){overflow:hidden}
figure{margin:0}
fieldset{border:1px solid silver;margin:0 2px;padding:.35em .625em .75em}
legend{border:0;padding:0}
button,input,select,textarea{font-family:inherit;font-size:100%;margin:0}
button,input{line-height:normal}
button,select{text-transform:none}
button,html input[type="button"],input[type="reset"],input[type="submit"]{-webkit-appearance:button;cursor:pointer}
button[disabled],html input[disabled]{cursor:default}
input[type="checkbox"],input[type="radio"]{box-sizing:border-box;padding:0}
input[type="search"]{-webkit-appearance:textfield;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;box-sizing:content-box}
input[type="search"]::-webkit-search-cancel-button,input[type="search"]::-webkit-search-decoration{-webkit-appearance:none}
button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}
textarea{overflow:auto;vertical-align:top}
table{border-collapse:collapse;border-spacing:0}
*,*:before,*:after{-moz-box-sizing:border-box;-webkit-box-sizing:border-box;box-sizing:border-box}
html,body{font-size:100%}
body{background:#fff;color:rgba(0,0,0,.8);padding:0;margin:0;font-family:"Noto Serif","DejaVu Serif",serif;font-weight:400;font-style:normal;line-height:1;position:relative;cursor:auto}
a:hover{cursor:pointer}
img,object,embed{max-width:100%;height:auto}
object,embed{height:100%}
img{-ms-interpolation-mode:bicubic}
.left{float:left!important}
.right{float:right!important}
.text-left{text-align:left!important}
.text-right{text-align:right!important}
.text-center{text-align:center!important}
.text-justify{text-align:justify!important}
.hide{display:none}
body{-webkit-font-smoothing:antialiased}
img,object,svg{display:inline-block;vertical-align:middle}
textarea{height:auto;min-height:50px}
select{width:100%}
.center{margin-left:auto;margin-right:auto}
.spread{width:100%}
p.lead,.paragraph.lead>p,#preamble>.sectionbody>.paragraph:first-of-type p{font-size:1.21875em;line-height:1.6}
.subheader,.admonitionblock td.content>.title,.audioblock>.title,.exampleblock>.title,.imageblock>.title,.listingblock>.title,.literalblock>.title,.stemblock>.title,.openblock>.title,.paragraph>.title,.quoteblock>.title,table.tableblock>.title,.verseblock>.title,.videoblock>.title,.dlist>.title,.olist>.title,.ulist>.title,.qlist>.title,.hdlist>.title{line-height:1.45;color:#7a2518;font-weight:400;margin-top:0;margin-bottom:.25em}
div,dl,dt,dd,ul,ol,li,h1,h2,h3,#toctitle,.sidebarblock>.content>.title,h4,h5,h6,pre,form,p,blockquote,th,td{margin:0;padding:0;direction:ltr}
a{color:#2156a5;text-decoration:underline;line-height:inherit}
a:hover,a:focus{color:#1d4b8f}
a img{border:none}
p{font-family:inherit;font-weight:400;font-size:1em;line-height:1.6;margin-bottom:1.25em;text-rendering:optimizeLegibility}
p aside{font-size:.875em;line-height:1.35;font-style:italic}
h1,h2,h3,#toctitle,.sidebarblock>.content>.title,h4,h5,h6{font-family:"Open Sans","DejaVu Sans",sans-serif;font-weight:300;font-style:normal;color:#ba3925;text-rendering:optimizeLegibility;margin-top:1em;margin-bottom:.5em;line-height:1.0125em}
h1 small,h2 small,h3 small,#toctitle small,.sidebarblock>.content>.title small,h4 small,h5 small,h6 small{font-size:60%;color:#e99b8f;line-height:0}
h1{font-size:2.125em}
h2{font-size:1.6875em}
h3,#toctitle,.sidebarblock>.content>.title{font-size:1.375em}
h4,h5{font-size:1.125em}
h6{font-size:1em}
hr{border:solid #ddddd8;border-width:1px 0 0;clear:both;margin:1.25em 0 1.1875em;height:0}
em,i{font-style:italic;line-height:inherit}
strong,b{font-weight:bold;line-height:inherit}
small{font-size:60%;line-height:inherit}
code{font-family:"Droid Sans Mono","DejaVu Sans Mono",monospace;font-weight:400;color:rgba(0,0,0,.9)}
ul,ol,dl{font-size:1em;line-height:1.6;margin-bottom:1.25em;list-style-position:outside;font-family:inherit}
ul,ol,ul.no-bullet,ol.no-bullet{margin-left:1.5em}
ul li ul,ul li ol{margin-left:1.25em;margin-bottom:0;font-size:1em}
ul.square li ul,ul.circle li ul,ul.disc li ul{list-style:inherit}
ul.square{list-style-type:square}
ul.circle{list-style-type:circle}
ul.disc{list-style-type:disc}
ul.no-bullet{list-style:none}
ol li ul,ol li ol{margin-left:1.25em;margin-bottom:0}
dl dt{margin-bottom:.3125em;font-weight:bold}
dl dd{margin-bottom:1.25em}
abbr,acronym{text-transform:uppercase;font-size:90%;color:rgba(0,0,0,.8);border-bottom:1px dotted #ddd;cursor:help}
abbr{text-transform:none}
blockquote{margin:0 0 1.25em;padding:.5625em 1.25em 0 1.1875em;border-left:1px solid #ddd}
blockquote cite{display:block;font-size:.9375em;color:rgba(0,0,0,.6)}
blockquote cite:before{content:"\2014 \0020"}
blockquote cite a,blockquote cite a:visited{color:rgba(0,0,0,.6)}
blockquote,blockquote p{line-height:1.6;color:rgba(0,0,0,.85)}
@media only screen and (min-width:768px){h1,h2,h3,#toctitle,.sidebarblock>.content>.title,h4,h5,h6{line-height:1.2}
h1{font-size:2.75em}
h2{font-size:2.3125em}
h3,#toctitle,.sidebarblock>.content>.title{font-size:1.6875em}
h4{font-size:1.4375em}}
table{background:#fff;margin-bottom:1.25em;border:solid 1px #dedede}
table thead,table tfoot{background:#f7f8f7;font-weight:bold}
table thead tr th,table thead tr td,table tfoot tr th,table tfoot tr td{padding:.5em .625em .625em;font-size:inherit;color:rgba(0,0,0,.8);text-align:left}
table tr th,table tr td{padding:.5625em .625em;font-size:inherit;color:rgba(0,0,0,.8)}
table tr.even,table tr.alt,table tr:nth-of-type(even){background:#f8f8f7}
table thead tr th,table tfoot tr th,table tbody tr td,table tr td,table tfoot tr td{display:table-cell;line-height:1.6}
body{tab-size:4}
h1,h2,h3,#toctitle,.sidebarblock>.content>.title,h4,h5,h6{line-height:1.2;word-spacing:-.05em}
h1 strong,h2 strong,h3 strong,#toctitle strong,.sidebarblock>.content>.title strong,h4 strong,h5 strong,h6 strong{font-weight:400}
.clearfix:before,.clearfix:after,.float-group:before,.float-group:after{content:" ";display:table}
.clearfix:after,.float-group:after{clear:both}
*:not(pre)>code{font-size:.9375em;font-style:normal!important;letter-spacing:0;padding:.1em .5ex;word-spacing:-.15em;background-color:#f7f7f8;-webkit-border-radius:4px;border-radius:4px;line-height:1.45;text-rendering:optimizeSpeed}
pre,pre>code{line-height:1.45;color:rgba(0,0,0,.9);font-family:"Droid Sans Mono","DejaVu Sans Mono",monospace;font-weight:400;text-rendering:optimizeSpeed}
.keyseq{color:rgba(51,51,51,.8)}
kbd{font-family:"Droid Sans Mono","DejaVu Sans Mono",monospace;display:inline-block;color:rgba(0,0,0,.8);font-size:.65em;line-height:1.45;background-color:#f7f7f7;border:1px solid #ccc;-webkit-border-radius:3px;border-radius:3px;-webkit-box-shadow:0 1px 0 rgba(0,0,0,.2),0 0 0 .1em white inset;box-shadow:0 1px 0 rgba(0,0,0,.2),0 0 0 .1em #fff inset;margin:0 .15em;padding:.2em .5em;vertical-align:middle;position:relative;top:-.1em;white-space:nowrap}
.keyseq kbd:first-child{margin-left:0}
.keyseq kbd:last-child{margin-right:0}
.menuseq,.menu{color:rgba(0,0,0,.8)}
b.button:before,b.button:after{position:relative;top:-1px;font-weight:400}
b.button:before{content:"[";padding:0 3px 0 2px}
b.button:after{content:"]";padding:0 2px 0 3px}
p a>code:hover{color:rgba(0,0,0,.9)}
#header,#content,#footnotes,#footer{width:100%;margin-left:auto;margin-right:auto;margin-top:0;margin-bottom:0;max-width:62.5em;*zoom:1;position:relative;padding-left:.9375em;padding-right:.9375em}
#header:before,#header:after,#content:before,#content:after,#footnotes:before,#footnotes:after,#footer:before,#footer:after{content:" ";display:table}
#header:after,#content:after,#footnotes:after,#footer:after{clear:both}
#content{margin-top:1.25em}
#content:before{content:none}
#header>h1:first-child{color:rgba(0,0,0,.85);margin-top:2.25rem;margin-bottom:0}
#header>h1:first-child+#toc{margin-top:8px;border-top:1px solid #ddddd8}
#header>h1:only-child,body.toc2 #header>h1:nth-last-child(2){border-bottom:1px solid #ddddd8;padding-bottom:8px}
#header .details{border-bottom:1px solid #ddddd8;line-height:1.45;padding-top:.25em;padding-bottom:.25em;padding-left:.25em;color:rgba(0,0,0,.6);display:-ms-flexbox;display:-webkit-flex;display:flex;-ms-flex-flow:row wrap;-webkit-flex-flow:row wrap;flex-flow:row wrap}
#header .details span:first-child{margin-left:-.125em}
#header .details span.email a{color:rgba(0,0,0,.85)}
#header .details br{display:none}
#header .details br+span:before{content:"\00a0\2013\00a0"}
#header .details br+span.author:before{content:"\00a0\22c5\00a0";color:rgba(0,0,0,.85)}
#header .details br+span#revremark:before{content:"\00a0|\00a0"}
#header #revnumber{text-transform:capitalize}
#header #revnumber:after{content:"\00a0"}
#content>h1:first-child:not([class]){color:rgba(0,0,0,.85);border-bottom:1px solid #ddddd8;padding-bottom:8px;margin-top:0;padding-top:1rem;margin-bottom:1.25rem}
#toc{border-bottom:1px solid #efefed;padding-bottom:.5em}
#toc>ul{margin-left:.125em}
#toc ul.sectlevel0>li>a{font-style:italic}
#toc ul.sectlevel0 ul.sectlevel1{margin:.5em 0}
#toc ul{font-family:"Open Sans","DejaVu Sans",sans-serif;list-style-type:none}
#toc li{line-height:1.3334;margin-top:.3334em}
#toc a{text-decoration:none}
#toc a:active{text-decoration:underline}
#toctitle{color:#7a2518;font-size:1.2em}
@media only screen and (min-width:768px){#toctitle{font-size:1.375em}
body.toc2{padding-left:15em;padding-right:0}
#toc.toc2{margin-top:0!important;background-color:#f8f8f7;position:fixed;width:15em;left:0;top:0;border-right:1px solid #efefed;border-top-width:0!important;border-bottom-width:0!important;z-index:1000;padding:1.25em 1em;height:100%;overflow:auto}
#toc.toc2 #toctitle{margin-top:0;margin-bottom:.8rem;font-size:1.2em}
#toc.toc2>ul{font-size:.9em;margin-bottom:0}
#toc.toc2 ul ul{margin-left:0;padding-left:1em}
#toc.toc2 ul.sectlevel0 ul.sectlevel1{padding-left:0;margin-top:.5em;margin-bottom:.5em}
body.toc2.toc-right{padding-left:0;padding-right:15em}
body.toc2.toc-right #toc.toc2{border-right-width:0;border-left:1px solid #efefed;left:auto;right:0}}
@media only screen and (min-width:1280px){body.toc2{padding-left:20em;padding-right:0}
#toc.toc2{width:20em}
#toc.toc2 #toctitle{font-size:1.375em}
#toc.toc2>ul{font-size:.95em}
#toc.toc2 ul ul{padding-left:1.25em}
body.toc2.toc-right{padding-left:0;padding-right:20em}}
#content #toc{border-style:solid;border-width:1px;border-color:#e0e0dc;margin-bottom:1.25em;padding:1.25em;background:#f8f8f7;-webkit-border-radius:4px;border-radius:4px}
#content #toc>:first-child{margin-top:0}
#content #toc>:last-child{margin-bottom:0}
#footer{max-width:100%;background-color:rgba(0,0,0,.8);padding:1.25em}
#footer-text{color:rgba(255,255,255,.8);line-height:1.44}
.sect1{padding-bottom:.625em}
@media only screen and (min-width:768px){.sect1{padding-bottom:1.25em}}
.sect1+.sect1{border-top:1px solid #efefed}
#content h1>a.anchor,h2>a.anchor,h3>a.anchor,#toctitle>a.anchor,.sidebarblock>.content>.title>a.anchor,h4>a.anchor,h5>a.anchor,h6>a.anchor{position:absolute;z-index:1001;width:1.5ex;margin-left:-1.5ex;display:block;text-decoration:none!important;visibility:hidden;text-align:center;font-weight:400}
#content h1>a.anchor:before,h2>a.anchor:before,h3>a.anchor:before,#toctitle>a.anchor:before,.sidebarblock>.content>.title>a.anchor:before,h4>a.anchor:before,h5>a.anchor:before,h6>a.anchor:before{content:"\00A7";font-size:.85em;display:block;padding-top:.1em}
#content h1:hover>a.anchor,#content h1>a.anchor:hover,h2:hover>a.anchor,h2>a.anchor:hover,h3:hover>a.anchor,#toctitle:hover>a.anchor,.sidebarblock>.content>.title:hover>a.anchor,h3>a.anchor:hover,#toctitle>a.anchor:hover,.sidebarblock>.content>.title>a.anchor:hover,h4:hover>a.anchor,h4>a.anchor:hover,h5:hover>a.anchor,h5>a.anchor:hover,h6:hover>a.anchor,h6>a.anchor:hover{visibility:visible}
#content h1>a.link,h2>a.link,h3>a.link,#toctitle>a.link,.sidebarblock>.content>.title>a.link,h4>a.link,h5>a.link,h6>a.link{color:#ba3925;text-decoration:none}
#content h1>a.link:hover,h2>a.link:hover,h3>a.link:hover,#toctitle>a.link:hover,.sidebarblock>.content>.title>a.link:hover,h4>a.link:hover,h5>a.link:hover,h6>a.link:hover{color:#a53221}
.audioblock,.imageblock,.literalblock,.listingblock,.stemblock,.videoblock{margin-bottom:1.25em}
.admonitionblock td.content>.title,.audioblock>.title,.exampleblock>.title,.imageblock>.title,.listingblock>.title,.literalblock>.title,.stemblock>.title,.openblock>.title,.paragraph>.title,.quoteblock>.title,table.tableblock>.title,.verseblock>.title,.videoblock>.title,.dlist>.title,.olist>.title,.ulist>.title,.qlist>.title,.hdlist>.title{text-rendering:optimizeLegibility;text-align:left;font-family:"Noto Serif","DejaVu Serif",serif;font-size:1rem;font-style:italic}
table.tableblock>caption.title{white-space:nowrap;overflow:visible;max-width:0}
.paragraph.lead>p,#preamble>.sectionbody>.paragraph:first-of-type p{color:rgba(0,0,0,.85)}
table.tableblock #preamble>.sectionbody>.paragraph:first-of-type p{font-size:inherit}
.admonitionblock>table{border-collapse:separate;border:0;background:none;width:100%}
.admonitionblock>table td.icon{text-align:center;width:80px}
.admonitionblock>table td.icon img{max-width:none}
.admonitionblock>table td.icon .title{font-weight:bold;font-family:"Open Sans","DejaVu Sans",sans-serif;text-transform:uppercase}
.admonitionblock>table td.content{padding-left:1.125em;padding-right:1.25em;border-left:1px solid #ddddd8;color:rgba(0,0,0,.6)}
.admonitionblock>table td.content>:last-child>:last-child{margin-bottom:0}
.exampleblock>.content{border-style:solid;border-width:1px;border-color:#e6e6e6;margin-bottom:1.25em;padding:1.25em;background:#fff;-webkit-border-radius:4px;border-radius:4px}
.exampleblock>.content>:first-child{margin-top:0}
.exampleblock>.content>:last-child{margin-bottom:0}
.sidebarblock{border-style:solid;border-width:1px;border-color:#e0e0dc;margin-bottom:1.25em;padding:1.25em;background:#f8f8f7;-webkit-border-radius:4px;border-radius:4px}
.sidebarblock>:first-child{margin-top:0}
.sidebarblock>:last-child{margin-bottom:0}
.sidebarblock>.content>.title{color:#7a2518;margin-top:0;text-align:center}
.exampleblock>.content>:last-child>:last-child,.exampleblock>.content .olist>ol>li:last-child>:last-child,.exampleblock>.content .ulist>ul>li:last-child>:last-child,.exampleblock>.content .qlist>ol>li:last-child>:last-child,.sidebarblock>.content>:last-child>:last-child,.sidebarblock>.content .olist>ol>li:last-child>:last-child,.sidebarblock>.content .ulist>ul>li:last-child>:last-child,.sidebarblock>.content .qlist>ol>li:last-child>:last-child{margin-bottom:0}
.literalblock pre,.listingblock pre:not(.highlight),.listingblock pre[class="highlight"],.listingblock pre[class^="highlight "],.listingblock pre.CodeRay,.listingblock pre.prettyprint{background:#f7f7f8}
.sidebarblock .literalblock pre,.sidebarblock .listingblock pre:not(.highlight),.sidebarblock .listingblock pre[class="highlight"],.sidebarblock .listingblock pre[class^="highlight "],.sidebarblock .listingblock pre.CodeRay,.sidebarblock .listingblock pre.prettyprint{background:#f2f1f1}
.literalblock pre,.literalblock pre[class],.listingblock pre,.listingblock pre[class]{-webkit-border-radius:4px;border-radius:4px;word-wrap:break-word;padding:1em;font-size:.8125em}
.literalblock pre.nowrap,.literalblock pre[class].nowrap,.listingblock pre.nowrap,.listingblock pre[class].nowrap{overflow-x:auto;white-space:pre;word-wrap:normal}
@media only screen and (min-width:768px){.literalblock pre,.literalblock pre[class],.listingblock pre,.listingblock pre[class]{font-size:.90625em}}
@media only screen and (min-width:1280px){.literalblock pre,.literalblock pre[class],.listingblock pre,.listingblock pre[class]{font-size:1em}}
.literalblock.output pre{color:#f7f7f8;background-color:rgba(0,0,0,.9)}
.listingblock pre.highlightjs{padding:0}
.listingblock pre.highlightjs>code{padding:1em;-webkit-border-radius:4px;border-radius:4px}
.listingblock pre.prettyprint{border-width:0}
.listingblock>.content{position:relative}
.listingblock code[data-lang]:before{display:none;content:attr(data-lang);position:absolute;font-size:.75em;top:.425rem;right:.5rem;line-height:1;text-transform:uppercase;color:#999}
.listingblock:hover code[data-lang]:before{display:block}
.listingblock.terminal pre .command:before{content:attr(data-prompt);padding-right:.5em;color:#999}
.listingblock.terminal pre .command:not([data-prompt]):before{content:"$"}
table.pyhltable{border-collapse:separate;border:0;margin-bottom:0;background:none}
table.pyhltable td{vertical-align:top;padding-top:0;padding-bottom:0;line-height:1.45}
table.pyhltable td.code{padding-left:.75em;padding-right:0}
pre.pygments .lineno,table.pyhltable td:not(.code){color:#999;padding-left:0;padding-right:.5em;border-right:1px solid #ddddd8}
pre.pygments .lineno{display:inline-block;margin-right:.25em}
table.pyhltable .linenodiv{background:none!important;padding-right:0!important}
.quoteblock{margin:0 1em 1.25em 1.5em;display:table}
.quoteblock>.title{margin-left:-1.5em;margin-bottom:.75em}
.quoteblock blockquote,.quoteblock blockquote p{color:rgba(0,0,0,.85);font-size:1.15rem;line-height:1.75;word-spacing:.1em;letter-spacing:0;font-style:italic;text-align:justify}
.quoteblock blockquote{margin:0;padding:0;border:0}
.quoteblock blockquote:before{content:"\201c";float:left;font-size:2.75em;font-weight:bold;line-height:.6em;margin-left:-.6em;color:#7a2518;text-shadow:0 1px 2px rgba(0,0,0,.1)}
.quoteblock blockquote>.paragraph:last-child p{margin-bottom:0}
.quoteblock .attribution{margin-top:.5em;margin-right:.5ex;text-align:right}
.quoteblock .quoteblock{margin-left:0;margin-right:0;padding:.5em 0;border-left:3px solid rgba(0,0,0,.6)}
.quoteblock .quoteblock blockquote{padding:0 0 0 .75em}
.quoteblock .quoteblock blockquote:before{display:none}
.verseblock{margin:0 1em 1.25em 1em}
.verseblock pre{font-family:"Open Sans","DejaVu Sans",sans;font-size:1.15rem;color:rgba(0,0,0,.85);font-weight:300;text-rendering:optimizeLegibility}
.verseblock pre strong{font-weight:400}
.verseblock .attribution{margin-top:1.25rem;margin-left:.5ex}
.quoteblock .attribution,.verseblock .attribution{font-size:.9375em;line-height:1.45;font-style:italic}
.quoteblock .attribution br,.verseblock .attribution br{display:none}
.quoteblock .attribution cite,.verseblock .attribution cite{display:block;letter-spacing:-.025em;color:rgba(0,0,0,.6)}
.quoteblock.abstract{margin:0 0 1.25em 0;display:block}
.quoteblock.abstract blockquote,.quoteblock.abstract blockquote p{text-align:left;word-spacing:0}
.quoteblock.abstract blockquote:before,.quoteblock.abstract blockquote p:first-of-type:before{display:none}
table.tableblock{max-width:100%;border-collapse:separate}
table.tableblock td>.paragraph:last-child p>p:last-child,table.tableblock th>p:last-child,table.tableblock td>p:last-child{margin-bottom:0}
table.tableblock,th.tableblock,td.tableblock{border:0 solid #dedede}
table.grid-all th.tableblock,table.grid-all td.tableblock{border-width:0 1px 1px 0}
table.grid-all tfoot>tr>th.tableblock,table.grid-all tfoot>tr>td.tableblock{border-width:1px 1px 0 0}
table.grid-cols th.tableblock,table.grid-cols td.tableblock{border-width:0 1px 0 0}
table.grid-all *>tr>.tableblock:last-child,table.grid-cols *>tr>.tableblock:last-child{border-right-width:0}
table.grid-rows th.tableblock,table.grid-rows td.tableblock{border-width:0 0 1px 0}
table.grid-all tbody>tr:last-child>th.tableblock,table.grid-all tbody>tr:last-child>td.tableblock,table.grid-all thead:last-child>tr>th.tableblock,table.grid-rows tbody>tr:last-child>th.tableblock,table.grid-rows tbody>tr:last-child>td.tableblock,table.grid-rows thead:last-child>tr>th.tableblock{border-bottom-width:0}
table.grid-rows tfoot>tr>th.tableblock,table.grid-rows tfoot>tr>td.tableblock{border-width:1px 0 0 0}
table.frame-all{border-width:1px}
table.frame-sides{border-width:0 1px}
table.frame-topbot{border-width:1px 0}
th.halign-left,td.halign-left{text-align:left}
th.halign-right,td.halign-right{text-align:right}
th.halign-center,td.halign-center{text-align:center}
th.valign-top,td.valign-top{vertical-align:top}
th.valign-bottom,td.valign-bottom{vertical-align:bottom}
th.valign-middle,td.valign-middle{vertical-align:middle}
table thead th,table tfoot th{font-weight:bold}
tbody tr th{display:table-cell;line-height:1.6;background:#f7f8f7}
tbody tr th,tbody tr th p,tfoot tr th,tfoot tr th p{color:rgba(0,0,0,.8);font-weight:bold}
p.tableblock>code:only-child{background:none;padding:0}
p.tableblock{font-size:1em}
td>div.verse{white-space:pre}
ol{margin-left:1.75em}
ul li ol{margin-left:1.5em}
dl dd{margin-left:1.125em}
dl dd:last-child,dl dd:last-child>:last-child{margin-bottom:0}
ol>li p,ul>li p,ul dd,ol dd,.olist .olist,.ulist .ulist,.ulist .olist,.olist .ulist{margin-bottom:.625em}
ul.unstyled,ol.unnumbered,ul.checklist,ul.none{list-style-type:none}
ul.unstyled,ol.unnumbered,ul.checklist{margin-left:.625em}
ul.checklist li>p:first-child>.fa-square-o:first-child,ul.checklist li>p:first-child>.fa-check-square-o:first-child{width:1em;font-size:.85em}
ul.checklist li>p:first-child>input[type="checkbox"]:first-child{width:1em;position:relative;top:1px}
ul.inline{margin:0 auto .625em auto;margin-left:-1.375em;margin-right:0;padding:0;list-style:none;overflow:hidden}
ul.inline>li{list-style:none;float:left;margin-left:1.375em;display:block}
ul.inline>li>*{display:block}
.unstyled dl dt{font-weight:400;font-style:normal}
ol.arabic{list-style-type:decimal}
ol.decimal{list-style-type:decimal-leading-zero}
ol.loweralpha{list-style-type:lower-alpha}
ol.upperalpha{list-style-type:upper-alpha}
ol.lowerroman{list-style-type:lower-roman}
ol.upperroman{list-style-type:upper-roman}
ol.lowergreek{list-style-type:lower-greek}
.hdlist>table,.colist>table{border:0;background:none}
.hdlist>table>tbody>tr,.colist>table>tbody>tr{background:none}
td.hdlist1,td.hdlist2{vertical-align:top;padding:0 .625em}
td.hdlist1{font-weight:bold;padding-bottom:1.25em}
.literalblock+.colist,.listingblock+.colist{margin-top:-.5em}
.colist>table tr>td:first-of-type{padding:0 .75em;line-height:1}
.colist>table tr>td:last-of-type{padding:.25em 0}
.thumb,.th{line-height:0;display:inline-block;border:solid 4px #fff;-webkit-box-shadow:0 0 0 1px #ddd;box-shadow:0 0 0 1px #ddd}
.imageblock.left,.imageblock[style*="float: left"]{margin:.25em .625em 1.25em 0}
.imageblock.right,.imageblock[style*="float: right"]{margin:.25em 0 1.25em .625em}
.imageblock>.title{margin-bottom:0}
.imageblock.thumb,.imageblock.th{border-width:6px}
.imageblock.thumb>.title,.imageblock.th>.title{padding:0 .125em}
.image.left,.image.right{margin-top:.25em;margin-bottom:.25em;display:inline-block;line-height:0}
.image.left{margin-right:.625em}
.image.right{margin-left:.625em}
a.image{text-decoration:none;display:inline-block}
a.image object{pointer-events:none}
sup.footnote,sup.footnoteref{font-size:.875em;position:static;vertical-align:super}
sup.footnote a,sup.footnoteref a{text-decoration:none}
sup.footnote a:active,sup.footnoteref a:active{text-decoration:underline}
#footnotes{padding-top:.75em;padding-bottom:.75em;margin-bottom:.625em}
#footnotes hr{width:20%;min-width:6.25em;margin:-.25em 0 .75em 0;border-width:1px 0 0 0}
#footnotes .footnote{padding:0 .375em 0 .225em;line-height:1.3334;font-size:.875em;margin-left:1.2em;text-indent:-1.05em;margin-bottom:.2em}
#footnotes .footnote a:first-of-type{font-weight:bold;text-decoration:none}
#footnotes .footnote:last-of-type{margin-bottom:0}
#content #footnotes{margin-top:-.625em;margin-bottom:0;padding:.75em 0}
.gist .file-data>table{border:0;background:#fff;width:100%;margin-bottom:0}
.gist .file-data>table td.line-data{width:99%}
div.unbreakable{page-break-inside:avoid}
.big{font-size:larger}
.small{font-size:smaller}
.underline{text-decoration:underline}
.overline{text-decoration:overline}
.line-through{text-decoration:line-through}
.aqua{color:#00bfbf}
.aqua-background{background-color:#00fafa}
.black{color:#000}
.black-background{background-color:#000}
.blue{color:#0000bf}
.blue-background{background-color:#0000fa}
.fuchsia{color:#bf00bf}
.fuchsia-background{background-color:#fa00fa}
.gray{color:#606060}
.gray-background{background-color:#7d7d7d}
.green{color:#006000}
.green-background{background-color:#007d00}
.lime{color:#00bf00}
.lime-background{background-color:#00fa00}
.maroon{color:#600000}
.maroon-background{background-color:#7d0000}
.navy{color:#000060}
.navy-background{background-color:#00007d}
.olive{color:#606000}
.olive-background{background-color:#7d7d00}
.purple{color:#600060}
.purple-background{background-color:#7d007d}
.red{color:#bf0000}
.red-background{background-color:#fa0000}
.silver{color:#909090}
.silver-background{background-color:#bcbcbc}
.teal{color:#006060}
.teal-background{background-color:#007d7d}
.white{color:#bfbfbf}
.white-background{background-color:#fafafa}
.yellow{color:#bfbf00}
.yellow-background{background-color:#fafa00}
span.icon>.fa{cursor:default}
.admonitionblock td.icon [class^="fa icon-"]{font-size:2.5em;text-shadow:1px 1px 2px rgba(0,0,0,.5);cursor:default}
.admonitionblock td.icon .icon-note:before{content:"\f05a";color:#19407c}
.admonitionblock td.icon .icon-tip:before{content:"\f0eb";text-shadow:1px 1px 2px rgba(155,155,0,.8);color:#111}
.admonitionblock td.icon .icon-warning:before{content:"\f071";color:#bf6900}
.admonitionblock td.icon .icon-caution:before{content:"\f06d";color:#bf3400}
.admonitionblock td.icon .icon-important:before{content:"\f06a";color:#bf0000}
.conum[data-value]{display:inline-block;color:#fff!important;background-color:rgba(0,0,0,.8);-webkit-border-radius:100px;border-radius:100px;text-align:center;font-size:.75em;width:1.67em;height:1.67em;line-height:1.67em;font-family:"Open Sans","DejaVu Sans",sans-serif;font-style:normal;font-weight:bold}
.conum[data-value] *{color:#fff!important}
.conum[data-value]+b{display:none}
.conum[data-value]:after{content:attr(data-value)}
pre .conum[data-value]{position:relative;top:-.125em}
b.conum *{color:inherit!important}
.conum:not([data-value]):empty{display:none}
dt,th.tableblock,td.content,div.footnote{text-rendering:optimizeLegibility}
h1,h2,p,td.content,span.alt{letter-spacing:-.01em}
p strong,td.content strong,div.footnote strong{letter-spacing:-.005em}
p,blockquote,dt,td.content,span.alt{font-size:1.0625rem}
p{margin-bottom:1.25rem}
.sidebarblock p,.sidebarblock dt,.sidebarblock td.content,p.tableblock{font-size:1em}
.exampleblock>.content{background-color:#fffef7;border-color:#e0e0dc;-webkit-box-shadow:0 1px 4px #e0e0dc;box-shadow:0 1px 4px #e0e0dc}
.print-only{display:none!important}
@media print{@page{margin:1.25cm .75cm}
*{-webkit-box-shadow:none!important;box-shadow:none!important;text-shadow:none!important}
a{color:inherit!important;text-decoration:underline!important}
a.bare,a[href^="#"],a[href^="mailto:"]{text-decoration:none!important}
a[href^="http:"]:not(.bare):after,a[href^="https:"]:not(.bare):after{content:"(" attr(href) ")";display:inline-block;font-size:.875em;padding-left:.25em}
abbr[title]:after{content:" (" attr(title) ")"}
pre,blockquote,tr,img,object,svg{page-break-inside:avoid}
thead{display:table-header-group}
svg{max-width:100%}
p,blockquote,dt,td.content{font-size:1em;orphans:3;widows:3}
h2,h3,#toctitle,.sidebarblock>.content>.title{page-break-after:avoid}
#toc,.sidebarblock,.exampleblock>.content{background:none!important}
#toc{border-bottom:1px solid #ddddd8!important;padding-bottom:0!important}
.sect1{padding-bottom:0!important}
.sect1+.sect1{border:0!important}
#header>h1:first-child{margin-top:1.25rem}
body.book #header{text-align:center}
body.book #header>h1:first-child{border:0!important;margin:2.5em 0 1em 0}
body.book #header .details{border:0!important;display:block;padding:0!important}
body.book #header .details span:first-child{margin-left:0!important}
body.book #header .details br{display:block}
body.book #header .details br+span:before{content:none!important}
body.book #toc{border:0!important;text-align:left!important;padding:0!important;margin:0!important}
body.book #toc,body.book #preamble,body.book h1.sect0,body.book .sect1>h2{page-break-before:always}
.listingblock code[data-lang]:before{display:block}
#footer{background:none!important;padding:0 .9375em}
#footer-text{color:rgba(0,0,0,.6)!important;font-size:.9em}
.hide-on-print{display:none!important}
.print-only{display:block!important}
.hide-for-print{display:none!important}
.show-for-print{display:inherit!important}}
</style>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.6.3/css/font-awesome.min.css">
</head>
<body class="article toc2 toc-left">
<div id="header">
<h1>http router</h1>
<div id="toc" class="toc2">
<div id="toctitle">Table of Contents</div>
<ul class="sectlevel1">
<li><a href="#truehttp-router">http router 是什么</a></li>
<li><a href="#truehttp-router-2">http router 构造</a>
<ul class="sectlevel2">
<li><a href="#true-trie">普通的 trie</a></li>
<li><a href="#trueradix-tree">radix tree</a></li>
<li><a href="#true-radix-tree">多棵 radix tree</a></li>
</ul>
</li>
<li><a href="#truehttprouter">httprouter 中的一些概念</a>
<ul class="sectlevel2">
<li><a href="#truenode">node</a></li>
<li><a href="#truentype">nType</a></li>
<li><a href="#truepath">path</a></li>
<li><a href="#trueindices">indices</a></li>
<li><a href="#truewildchild">wildChild</a></li>
<li><a href="#truecatchall">catchAll</a></li>
</ul>
</li>
<li><a href="#truehttprouter-radix-tree">httprouter 中 radix tree 的构造过程</a>
<ul class="sectlevel2">
<li><a href="#true-">第一条路由</a></li>
<li><a href="#true--2">第二条路由</a></li>
<li><a href="#true--3">第三条路由</a></li>
</ul>
</li>
<li><a href="#true--4">路由冲突</a>
<ul class="sectlevel2">
<li><a href="#true--5">一个例子</a></li>
<li><a href="#true--6">所有情况</a></li>
</ul>
</li>
<li><a href="#true--7">路由功能扩展</a></li>
<li><a href="#true--8">路由库性能相关的评测</a></li>
<li><a href="#true--9">其它路由项目</a></li>
<li><a href="#true--10">注意事项</a></li>
<li><a href="#true-restful-graphql">拓展阅读：RESTFul 和 GraphQL 之争</a></li>
<li><a href="#truego">Go 夜读调查问卷</a></li>
</ul>
</div>
</div>
<div id="content">
<div class="sect1">
<h2 id="truehttp-router"><a class="anchor" href="#truehttp-router"></a>http router 是什么</h2>
<div class="sectionbody">
<div class="paragraph">
<p>从功能上来讲，就是 URI &#8594; handler 函数的映射。</p>
</div>
<div class="imageblock">
<div class="content">
<img src="url_to_handler.png" alt="url to handler.png">
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="truehttp-router-2"><a class="anchor" href="#truehttp-router-2"></a>http router 构造</h2>
<div class="sectionbody">
<div class="sect2">
<h3 id="true-trie"><a class="anchor" href="#true-trie"></a>普通的 trie</h3>
<div class="imageblock">
<div class="content">
<img src="trie.png" alt="trie.png">
</div>
</div>
<div class="ulist">
<ul>
<li>
<p>单个节点代表一个字母</p>
</li>
<li>
<p>如果需要对字符串进行匹配</p>
</li>
<li>
<p>只要从根节点开始依次匹配即可</p>
</li>
</ul>
</div>
<div class="paragraph">
<p>普通的 trie 有什么缺点呢？</p>
</div>
<div class="quoteblock">
<blockquote>
<div class="paragraph">
<p>在主要的英文字典中，最长的单词是 pneumonoultramicroscopicsilicovolcanoconiosis，由45个字母组成，意思是一种肺部疾病（由于吸入超显微硅酸盐及石英尘所引起的）肺尘埃沉着病”，通称火山矽肺病。其中，pneumo-表示“肺”，ultra-表示“超”，microscopic意为“微观”，silico-表示“硅”，volcano指“火山”，coni-意思是“尘”，-osis为表示疾病的后缀。后来研究者认为这是一个大骗局。</p>
</div>
</blockquote>
<div class="attribution">
&#8212; Wikipedia<br>
<cite>最长的英文单词</cite>
</div>
</div>
<div class="ulist">
<ul>
<li>
<p>树的深度和路由字符串长度正相关</p>
</li>
<li>
<p>占用较多的内存</p>
</li>
<li>
<p>字符串越长，匹配越慢(类似链表结构，在内存中存储不连续的数据)</p>
</li>
</ul>
</div>
</div>
<div class="sect2">
<h3 id="trueradix-tree"><a class="anchor" href="#trueradix-tree"></a>radix tree</h3>
<div class="quoteblock">
<blockquote>
<div class="paragraph">
<p>In computer science, a radix tree (also radix trie or compact prefix tree) is a data structure that represents a space-optimized trie (prefix tree) in which each node that is the only child is merged with its parent. The result is that the number of children of every internal node is at most the radix r of the radix tree, where r is a positive integer and a power x of 2, having x ≥ 1. Unlike regular tries, edges can be labeled with sequences of elements as well as single elements. This makes radix trees much more efficient for small sets (especially if the strings are long) and for sets of strings that share long prefixes.</p>
</div>
</blockquote>
<div class="attribution">
&#8212; Wikipedia
</div>
</div>
<div class="paragraph">
<p>在 http 路由的场景下 一棵 radix tree 是不够用的 为什么呢?</p>
</div>
<div class="sect3">
<h4 id="truerestful"><a class="anchor" href="#truerestful"></a>restful 风格路由</h4>
<div class="imageblock">
<div class="content">
<img src="entries.png" alt="entries.png">
</div>
</div>
<div class="paragraph">
<p>同一个 URI 会提供多个 HTTP 方法，以对“资源”进行创建、更新、删除、获取。如果我们只有一棵树，显然是没有办法支持的。</p>
</div>
<div class="paragraph">
<p>怎么解决这个问题呢？</p>
</div>
</div>
</div>
<div class="sect2">
<h3 id="true-radix-tree"><a class="anchor" href="#true-radix-tree"></a>多棵 radix tree</h3>
<div class="paragraph">
<p>GET 一棵，PUT 一棵，POST 一棵，以此类推：</p>
</div>
<div class="imageblock">
<div class="content">
<img src="radix_tree.png" alt="radix tree.png">
</div>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="truehttprouter"><a class="anchor" href="#truehttprouter"></a>httprouter 中的一些概念</h2>
<div class="sectionbody">
<div class="sect2">
<h3 id="truenode"><a class="anchor" href="#truenode"></a>node</h3>
<div class="paragraph">
<p>就是 httprouter 树中的节点。</p>
</div>
</div>
<div class="sect2">
<h3 id="truentype"><a class="anchor" href="#truentype"></a>nType</h3>
<div class="paragraph">
<p>就是 node type，有几种枚举值：</p>
</div>
<div class="ulist">
<ul>
<li>
<p>static                   // 非根节点的普通字符串节点</p>
</li>
<li>
<p>root                     // 根节点</p>
</li>
<li>
<p>param(wildcard)          // 参数节点，例如 :id</p>
</li>
<li>
<p>catchAll                 // 通配符节点，例如 *anyway</p>
</li>
</ul>
</div>
</div>
<div class="sect2">
<h3 id="truepath"><a class="anchor" href="#truepath"></a>path</h3>
<div class="paragraph">
<p>到达节点时，所经过的字符串路径。</p>
</div>
<div class="imageblock">
<div class="content">
<img src="path.png" alt="path.png">
</div>
</div>
</div>
<div class="sect2">
<h3 id="trueindices"><a class="anchor" href="#trueindices"></a>indices</h3>
<div class="paragraph">
<p>子节点索引，当子节点为非参数类型，即本节点的 wildChild 为 false 时，会将每个子节点的首字母放在该索引数组。说是数组，实际上是个 string。</p>
</div>
<div class="imageblock">
<div class="content">
<img src="indices.png" alt="indices.png">
</div>
</div>
<div class="paragraph">
<p>如果子节点为参数节点时，indices 应该是个空字符串。</p>
</div>
<div class="imageblock">
<div class="content">
<img src="indices2.png" alt="indices2.png">
</div>
</div>
</div>
<div class="sect2">
<h3 id="truewildchild"><a class="anchor" href="#truewildchild"></a>wildChild</h3>
<div class="paragraph">
<p>如果一个节点的子节点中有 param(wildcard) 节点，那么该节点的 wildChild 字段即为 true。</p>
</div>
</div>
<div class="sect2">
<h3 id="truecatchall"><a class="anchor" href="#truecatchall"></a>catchAll</h3>
<div class="paragraph">
<p>以 <code>*</code> 结尾的路由，即为 catchAll。在静态文件服务上，catchAll 用的比较多。后面的部分一般用来描述文件路径。如：/software/downloads/monodraw-latest.dmg。</p>
</div>
<div class="paragraph">
<p>得到后缀之后，就可以知道文件路径了。当然，现在的服务端文件系统可能是虚拟目录。看具体的实现了。</p>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="truehttprouter-radix-tree"><a class="anchor" href="#truehttprouter-radix-tree"></a>httprouter 中 radix tree 的构造过程</h2>
<div class="sectionbody">
<div class="paragraph">
<p>假设我们先后插入三条路由，这些路由都用相同的 http method(GET)</p>
</div>
<div class="sect2">
<h3 id="true-"><a class="anchor" href="#true-"></a>第一条路由</h3>
<div class="paragraph">
<p>插入 /marketplace_listing/plans/</p>
</div>
<div class="imageblock">
<div class="content">
<img src="node_insert1.png" alt="node insert1.png">
</div>
</div>
</div>
<div class="sect2">
<h3 id="true--2"><a class="anchor" href="#true--2"></a>第二条路由</h3>
<div class="paragraph">
<p>插入 /marketplace_listing/plans/:id/acounts</p>
</div>
<div class="imageblock">
<div class="content">
<img src="node_insert2.png" alt="node insert2.png">
</div>
</div>
</div>
<div class="sect2">
<h3 id="true--3"><a class="anchor" href="#true--3"></a>第三条路由</h3>
<div class="paragraph">
<p>插入 /search</p>
</div>
<div class="imageblock">
<div class="content">
<img src="node_insert3.png" alt="node insert3.png">
</div>
</div>
<div class="paragraph">
<p>在根节点上发生了边的分裂。</p>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="true--4"><a class="anchor" href="#true--4"></a>路由冲突</h2>
<div class="sectionbody">
<div class="paragraph">
<p>路由发生冲突，主要是 static 节点、param 节点、catchAll 节点之间冲突，例如：</p>
</div>
<div class="sect2">
<h3 id="true--5"><a class="anchor" href="#true--5"></a>一个例子</h3>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-c" data-lang="c">conflict:
GET /user/info/:name
GET /user/:id</code></pre>
</div>
</div>
<div class="paragraph">
<p>为什么会冲突呢？因为 param 节点和普通字符串节点是没有办法共存的。例如输入路由字符串为：<code>/user/info</code>，在 <code>/user/:id</code> 规则中，info 会被解释为 :id 的值。</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-c" data-lang="c">no conflict:
GET /user/info/:name
POST /user/:id</code></pre>
</div>
</div>
<div class="paragraph">
<p>两个路由的 HTTP Method(GET/POST) 不同，因此会在不同的 radix tree 上。</p>
</div>
</div>
<div class="sect2">
<h3 id="true--6"><a class="anchor" href="#true--6"></a>所有情况</h3>
<div class="ulist">
<ul>
<li>
<p>在插入 wildcard 节点时，父节点的 children 数组非空且 wildChild 被设置为 false。例如：GET /user/getAll 和 GET /user/:id/getAddr，或者 GET /user/*aaa和 GET /user/:id。</p>
</li>
<li>
<p>在插入 wildcard 节点时，父节点的 children 数组非空且 wildChild 被设置为 true，但该父节点的 wildcard 子节点要插入的 wildcard 名字不一样。例如： GET /user/:id/info 和 GET /user/:name/info。</p>
</li>
<li>
<p>在插入 catchAll 节点时，父节点的 children 非空。例如： GET /src/abc 和 GET /src/*filename，或者 GET /src/:id 和 GET /src/*filename。</p>
</li>
<li>
<p>在插入 static 节点时，父节点的 wildChild 字段被设置为 true。</p>
</li>
<li>
<p>在插入 static 节点时，父节点的 children 非空，且子节点 nType 为 catchAll。</p>
</li>
</ul>
</div>
<div class="paragraph">
<p>很好理解，能看懂下面这张图就行：</p>
</div>
<div class="imageblock">
<div class="content">
<img src="exclusive.png" alt="exclusive.png">
</div>
</div>
<div class="paragraph">
<p>即同一个节点，其子节点的情况只可能是：</p>
</div>
<div class="ulist">
<ul>
<li>
<p>一个 wildcard 节点</p>
</li>
<li>
<p>一个 catchAll 节点</p>
</li>
<li>
<p>一个或多个 static 节点</p>
</li>
</ul>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="true--7"><a class="anchor" href="#true--7"></a>路由功能扩展</h2>
<div class="sectionbody">
<div class="paragraph">
<p>上面我们看到，httprouter 中只有 static/param/checkAll 这三种节点。有一部分人认为功能不够强大。</p>
</div>
<div class="paragraph">
<p>我们可以思考如何对标准的 httprouter 功能进行扩展。</p>
</div>
<div class="paragraph">
<p>目前 param 节点和 static 节点无法共存，如果我们想让 param 和 static 可以共存的话呢？</p>
</div>
<div class="ulist">
<ul>
<li>
<p>/cars/{id : \d+}</p>
</li>
<li>
<p>/cars/f1</p>
</li>
</ul>
</div>
<div class="paragraph">
<p>上面这两条路由显然是可以共存的，我们可以先匹配 /cars/f1，不匹配的情况下再去尝试匹配 /cars/{id: \d+}，都不匹配，则 404。</p>
</div>
<div class="paragraph">
<p>想实现这个功能也不难，可以自行尝试。</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="true--8"><a class="anchor" href="#true--8"></a>路由库性能相关的评测</h2>
<div class="sectionbody">
<div class="paragraph">
<p><a href="https://github.com/julienschmidt/go-http-routing-benchmark" class="bare">https://github.com/julienschmidt/go-http-routing-benchmark</a></p>
</div>
<div class="paragraph">
<p><a href="https://github.com/julienschmidt/go-http-routing-benchmark/pull/82" class="bare">https://github.com/julienschmidt/go-http-routing-benchmark/pull/82</a></p>
</div>
<div class="paragraph">
<p><a href="https://github.com/smallnest/go-web-framework-benchmark" class="bare">https://github.com/smallnest/go-web-framework-benchmark</a></p>
</div>
<div class="paragraph">
<p><a href="https://www.reddit.com/r/golang/comments/71i2oh/how_are_various_routers_are_faster_than_each_other/" class="bare">https://www.reddit.com/r/golang/comments/71i2oh/how_are_various_routers_are_faster_than_each_other/</a></p>
</div>
<div class="imageblock">
<div class="content">
<img src="benchmark_latency.png" alt="benchmark latency.png">
</div>
</div>
<div class="admonitionblock tip">
<table>
<tr>
<td class="icon">
<i class="fa icon-tip" title="Tip"></i>
</td>
<td class="content">
<div class="paragraph">
<p>路由的性能对我们的线上项目有多大影响呢?</p>
</div>
</td>
</tr>
</table>
</div>
</div>
</div>
<div class="sect1">
<h2 id="true--9"><a class="anchor" href="#true--9"></a>其它路由项目</h2>
<div class="sectionbody">
<div class="paragraph">
<p>上面的 benchmark 里有，简单扫了扫代码，功能其实都差不多。</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="true--10"><a class="anchor" href="#true--10"></a>注意事项</h2>
<div class="sectionbody">
<div class="paragraph">
<p>httprouter 在路径中的 param 节点不能超过 255，这个结论怎么得到的呢？可以看看代码。</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="true-restful-graphql"><a class="anchor" href="#true-restful-graphql"></a>拓展阅读：RESTFul 和 GraphQL 之争</h2>
<div class="sectionbody">
<div class="paragraph">
<p><a href="https://www.howtographql.com/basics/1-graphql-is-the-better-rest/" class="bare">https://www.howtographql.com/basics/1-graphql-is-the-better-rest/</a></p>
</div>
<div class="paragraph">
<p>看看就行了。该 post 一把梭的时候连 RESTFul 都不用，更不用说 GraphQL 了。</p>
</div>
<div class="paragraph">
<p>对于有些公司的人来说，RESTFul 都接不利索(比如 Go 标准库中的 http client，用 PUT/PATCH 之类的方法就很不方便，开源的 http client 各种 bug)。</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="truego"><a class="anchor" href="#truego"></a>Go 夜读调查问卷</h2>
<div class="sectionbody">
<div class="imageblock">
<div class="content">
<img src="survay.png" alt="survay.png">
</div>
</div>
<div class="quoteblock">
<blockquote>
<div class="paragraph">
<p>Please do not obsess over routers. Their difference in speed, if any, is negligible compared to the network and disk IO of a standard web app. Just pick one and move on.</p>
</div>
</blockquote>
<div class="attribution">
&#8212; reddit
</div>
</div>
<div class="paragraph">
<p>分享过程中有同学问的没有回答的两个问题：</p>
</div>
<div class="admonitionblock tip">
<table>
<tr>
<td class="icon">
<i class="fa icon-tip" title="Tip"></i>
</td>
<td class="content">
<div class="paragraph">
<p>httprouter 是不是不支持 /:hashcode 这种路由？</p>
</div>
</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>是支持的，必须以 / 开头才行</p>
</div>
<div class="admonitionblock tip">
<table>
<tr>
<td class="icon">
<i class="fa icon-tip" title="Tip"></i>
</td>
<td class="content">
<div class="paragraph">
<p>这些路由的 zero garbage 和 0 alloc 是咋实现的？</p>
</div>
</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>zero garbage 指进程不在堆上分配内存。http router 实现了全静态路由时，匹配过程 0 alloc，这里的 0 次分配其实就是没有堆内存分配(栈上的肯定还是有的，但栈上分配内存不影响 GC)。</p>
</div>
<div class="paragraph">
<p>但 httprouter 在路由中含有参数时，会额外分配一个 Params 的 slice，提供给用户的 handler 来使用。所以可以看到，带参数的路由在 httprouter 的 benchmark 中有 alloc：</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-c" data-lang="c">BenchmarkHttpRouter_Param           20000000        139 ns/op          33 B/op        1 allocs/op</code></pre>
</div>
</div>
<div class="paragraph">
<p>下面是 gin 的 benchmark：</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-c" data-lang="c">BenchmarkGin_Param                      20000000               113 ns/op               0 B/op          0 allocs/op</code></pre>
</div>
</div>
<div class="paragraph">
<p>Gin 用的也是 httprouter，但是为什么这里却是 0 alloc 呢？答案很弱智，sync.Pool：</p>
</div>
<div class="paragraph">
<p><a href="https://github.com/gin-gonic/gin/issues/249" class="bare">https://github.com/gin-gonic/gin/issues/249</a></p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-c" data-lang="c">// Engine is the framework's instance, it contains the muxer, middleware and configuration settings.
// Create an instance of Engine, by using New() or Default()
type Engine struct {
	RouterGroup

      // ....
	pool             sync.Pool
      // ....
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>感兴趣的同学可以自己追一下代码。</p>
</div>
</div>
</div>
</div>
<div id="footer">
<div id="footer-text">
Last updated 2019-08-02 11:40:06 CST
</div>
</div>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/8.9.1/styles/github.min.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/8.9.1/highlight.min.js"></script>
<script>hljs.initHighlighting()</script>
</body>
</html>